package com.example.literj.kt.internal.util

import com.example.literj.kt.disposables.Disposable

class OpenHashSet {
    private val INT_PHI = -0x61c88647

    private val loadFactor: Float
    private var mask: Int
    private var size: Int = 0
    private var maxSize: Int
    private var keys: Array<Disposable?>

    constructor() : this(16)
    constructor(capacity: Int) : this(capacity, 0.75f)
    constructor(capacity: Int, loadFactor: Float) {
        this.loadFactor = loadFactor
        val c = Pow2.roundToPowerOfTwo(capacity)
        this.mask = c - 1
        this.maxSize = (loadFactor * c).toInt()
        this.keys = arrayOfNulls<Disposable?>(c)
    }

    fun add(value: Disposable?): Boolean {
        val a = keys
        val m = mask
        var pos = mix(value.hashCode()) and m
        var curr = a[pos]
        if (curr != null) {
            if (curr == value) {
                return false
            }
            while (true) {
                pos = pos + 1 and m
                curr = a[pos]
                if (curr == null) {
                    break
                }
                if (curr == value) {
                    return false
                }
            }
        }
        a[pos] = value
        if (++size >= maxSize) {
            rehash()
        }
        return true
    }

    fun remove(value: Disposable?): Boolean {
        val a = keys
        val m = mask
        var pos = mix(value.hashCode()) and m
        var curr: Disposable? = a[pos] ?: return false
        if (curr == value) {
            return removeEntry(pos, a, m)
        }
        while (true) {
            pos = pos + 1 and m
            curr = a[pos]
            if (curr == null) {
                return false
            }
            if (curr == value) {
                return removeEntry(pos, a, m)
            }
        }
    }

    fun removeEntry(position: Int, a: Array<Disposable?>, m: Int): Boolean {
        var pos = position
        size--
        var last: Int
        var slot: Int
        var curr: Disposable?
        while (true) {
            last = pos
            pos = pos + 1 and m
            while (true) {
                curr = a[pos]
                if (curr == null) {
                    a[last] = null
                    return true
                }
                slot = mix(curr.hashCode()) and m
                if (if (last <= pos) last >= slot || slot > pos else last >= slot && slot > pos) {
                    break
                }
                pos = pos + 1 and m
            }
            a[last] = curr
        }
    }

    fun rehash() {
        val a = keys
        var i = a.size
        val newCap = i shl 1
        val m = newCap - 1
        val b = arrayOfNulls<Disposable>(newCap)
        var j = size
        while (j-- != 0) {
            while (a[--i] == null) {
            }
            var pos = mix(a[i].hashCode()) and m
            if (b[pos] != null) {
                while (true) {
                    pos = pos + 1 and m
                    if (b[pos] == null) {
                        break
                    }
                }
            }
            b[pos] = a[i]
        }
        mask = m
        maxSize = (newCap * loadFactor).toInt()
        keys = b
    }

    fun mix(x: Int): Int {
        val h = x * INT_PHI
        return h xor (h ushr 16)
    }

    fun keys(): Array<Disposable?> {
        return keys
    }

    fun size(): Int {
        return size
    }
}